package com.easypay.reactive8583.netty.pipeline;

import com.easypay.reactive8583.ConnectorConfiguration;
import com.easypay.reactive8583.ConnectorConfigurer;
import com.easypay.reactive8583.iso.MessageFactory;
import com.easypay.reactive8583.netty.codec.Iso8583Decoder;
import com.easypay.reactive8583.netty.codec.Iso8583Encoder;
import com.easypay.reactive8583.netty.codec.StringLengthFieldBasedFrameDecoder;
import com.solab.iso8583.IsoMessage;
import io.netty.bootstrap.AbstractBootstrap;
import io.netty.channel.*;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.timeout.IdleStateHandler;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author pandans
 * @date 2021/03/04 16:44
 * @Description:
 */
public class Iso8583ChannelInitializer<T extends Channel, B extends AbstractBootstrap<?, ?>, C extends ConnectorConfiguration> extends ChannelInitializer<T> {
    private C configuration;
    private ConnectorConfigurer<C, B> configurer;
    private EventLoopGroup workerGroup;
    private MessageFactory<IsoMessage> isoMessageFactory;
    private List<ChannelHandler> customChannelHandlers = new ArrayList<>();

    private Iso8583Encoder isoMessageEncoder;
    private ChannelHandler loggingHandler;
    private ChannelHandler parseExceptionHandler;

    public Iso8583ChannelInitializer() {
        super();
    }

    public Iso8583ChannelInitializer(C configuration, ConnectorConfigurer<C, B> configurer, EventLoopGroup workerGroup, MessageFactory<IsoMessage> isoMessageFactory, ChannelHandler... customChannelHandlers) {
        this.configuration = configuration;
        this.configurer = configurer;
        this.workerGroup = workerGroup;
        this.isoMessageFactory = isoMessageFactory;
        this.customChannelHandlers = new ArrayList(Arrays.asList(customChannelHandlers));
        this.isoMessageEncoder = createIso8583Encoder(configuration);
        this.loggingHandler = createLoggingHandler(configuration);
        this.parseExceptionHandler = createParseExceptionHandler();
    }

    @Override
    public void initChannel(T ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(
                "lengthFieldFrameDecoder",
                createLengthFieldBasedFrameDecoder(configuration)
        );
        pipeline.addLast("iso8583Decoder", createIso8583Decoder(isoMessageFactory));
        pipeline.addLast("iso8583Encoder", isoMessageEncoder);
        if (configuration.isAddLoggingHandler()) {
            pipeline.addLast(workerGroup, "logging", loggingHandler);
        }
        if (configuration.isReplyOnError()) {
            pipeline.addLast(workerGroup, "replyOnError", parseExceptionHandler);
        }
        pipeline.addLast("idleState", new IdleStateHandler(0, 0, configuration.getIdleTimeout()));
        pipeline.addLast("idleEventHandler", new IdleEventHandler(isoMessageFactory));
        for(ChannelHandler customChannel : customChannelHandlers) {
            pipeline.addLast(workerGroup, customChannel);
        }
        if(configurer != null) {

        }

    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        super.handlerAdded(ctx);
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        super.handlerRemoved(ctx);
    }
    private Iso8583Encoder createIso8583Encoder(C configuration) {
        return new Iso8583Encoder(
                configuration.getFrameLengthFieldLength(),
                configuration.isEncodeFrameLengthAsString()
        );
    }

    private Iso8583Decoder createIso8583Decoder(MessageFactory<IsoMessage> messageFactory) {
        return new Iso8583Decoder(messageFactory);
    }
    private ChannelHandler createParseExceptionHandler()  {
        return new ParseExceptionHandler(isoMessageFactory, true);
    }
    private ChannelHandler createLoggingHandler(C configuration) {
        return new IsoMessageLoggingHandler(
                LogLevel.DEBUG,
                configuration.isLogSensitiveData(),
                configuration.isLogFieldDescription(),
                configuration.getSensitiveDataFields()
        );
    }

    protected ChannelHandler createLengthFieldBasedFrameDecoder(C configuration) {
        int lengthFieldLength = configuration.getFrameLengthFieldLength();
        if(configuration.isEncodeFrameLengthAsString()) {
            return new StringLengthFieldBasedFrameDecoder(configuration.getMaxFrameLength(), configuration.getFrameLengthFieldOffset(),
                    lengthFieldLength, configuration.getFrameLengthFieldAdjust(), lengthFieldLength);
        }else {
            return new LengthFieldBasedFrameDecoder(configuration.getMaxFrameLength(), configuration.getFrameLengthFieldOffset(),
                    lengthFieldLength, configuration.getFrameLengthFieldAdjust(), lengthFieldLength);
        }
    }

}
